pub extern crate linkme;
pub extern crate napih_derive as derive;
pub extern crate napih_sys as sys;

mod env;
mod linker;
mod module;
mod objs;
mod status;
mod value;

mod from;
mod into;
mod rules;

mod mem;

pub use env::*;
pub use linker::*;
pub use module::*;
pub use objs::*;
pub use status::*;
pub use value::*;

/// A prelude of traits and types for working with N-API
#[allow(unused_imports)]
pub mod prelude {
	pub use super::derive::*;
	pub use super::rules::*;
	pub use super::{FromNapi, IntoNapi, NapiClass, NapiDrop, NapiExternal, NapiThrow};
}

/// Trait for converting Rust types to N-API values
pub trait IntoNapi {
	fn into_napi<'env>(self, env: &'env Env) -> Result<'static, Value<'env>>;
}

/// Trait for converting N-API values to Rust types
pub trait FromNapi<'val> {
	fn from_napi(v: Value<'val>) -> Result<'static, Self>
	where
		Self: Sized;
}

/// Trait for custom cleanup of external N-API values
pub trait NapiDrop {
	fn drop_napi(self, _: &Env) -> Result<'_, ()>
	where
		Self: Sized,
	{
		Ok(())
	}
}

/// Trait for defining N-API classes with constructor support
pub trait NapiClass: NapiDrop + Sized + 'static {
	fn class() -> Option<Reference> {
		class_define(::core::any::TypeId::of::<Self>())
	}
	fn instance<A: ArgsBuilder>(env: &Env, args: A) -> Result<'static, Value<'_>> {
		env.instance::<Self, A>(args)
	}
	extern "C" fn __napih_constructor(env: sys::napi_env, info: sys::napi_callback_info) -> sys::napi_value {
		let Some(constructor) = class_constructor(core::any::TypeId::of::<Self>()) else {
			return core::ptr::null_mut();
		};
		for call in constructor.iter() {
			let Some(call) = call else {
				continue;
			};
			let ret = unsafe { call(env, info) };
			if !ret.is_null() {
				return ret;
			}
		}
		core::ptr::null_mut()
	}
}

/// Trait for converting Rust types to external N-API values
pub trait NapiExternal: NapiDrop + Sized + 'static {
	fn external<'env>(self, env: &'env Env) -> Result<'static, Value<'env>> {
		let data = mem::into_raw_data(self);
		let mut ret = core::ptr::null_mut();
		unsafe { sys::napi_create_external(env.raw(), data, Some(mem::finalizer::<Self>), core::ptr::null_mut(), &mut ret) }
			.result("napi_create_external")?;
		Ok(Value::new(env, ret))
	}
	extern "C" fn __napih_constructor(_: sys::napi_env, _: sys::napi_callback_info) -> sys::napi_value {
		::core::ptr::null_mut()
	}
}
